<template>
  <view class="container">
    <CustomNavbar
    />

    <!-- 全局加载遮罩（在数据载入完成前显示） -->
    <view v-if="!isLoaded" class="loading-overlay">
      <view class="loading-box">
        <view class="spinner">
          <view class="dot" v-for="n in 3" :key="n"></view>
        </view>
        <view class="loading-text">数据加载中...</view>
      </view>
    </view>

    <PublicContentLayout>
      <view class="banner-area" v-if="isShowLookBox">
        <view class="btn">
          <view class="btn-text" @tap="navigateToActivityRule">
            活动说明
          </view>
        </view>
      </view>
      <view class="info">
        <view class="info-left">
          <UserAvatar :avatar-image="userInfo.avatarImage" :title="userInfo.title" />
        </view>
        <view class="info-right">
          <view class="info-right-name">
            {{ userInfo.flowerName }}（{{ userInfo.workNumber }}）
            <view class="info-right-lb">{{ userInfo.track }}{{ userInfo.position }}</view>
          </view>
          <view class="info-right-city">{{ userInfo.primaryRegion }}-{{ userInfo.secondaryRegion }}</view>
          <!-- 兼容：tap 双击检测 + 原生 dblclick（桌面）+ longpress（移动长按） -->
          <view>
            <!-- 如果是当前用户：显示编辑入口（可切换到输入框进行编辑） -->
            <view v-if="isCurrentUser">
              <view v-if="!editingDeclaration" class="info-right-declaration" @tap="handleDeclarationTap" @dblclick="enableEditDeclaration" @dbltap="enableEditDeclaration" @longpress="enableEditDeclaration" @longtap="enableEditDeclaration">{{ userInfo.declaration || displayedDeclaration || '点击双击编辑你的宣言' }}</view>
              <input v-else ref="declarationInput" class="info-right-declaration-input" type="text" v-model="userInfo.declaration" @blur="submitDeclaration" @confirm="submitDeclaration" @keyup.enter="submitDeclaration" />
            </view>

            <!-- 非当前用户：仅在已有宣言时显示为只读，未设置则隐藏整个模块 -->
            <view v-else-if="(displayedDeclaration && String(displayedDeclaration).trim() !== '') || (userInfo.declaration && String(userInfo.declaration).trim() !== '')" class="info-right-declaration">{{ displayedDeclaration || userInfo.declaration }}</view>
          </view>
        </view>
      </view>
      <view class="data">
        <view class="data-top">
          <view class="data-top-title">本活动总得分:{{ userInfo.totalScore }}分</view>
          <view class="data-top-rank">
            <text>区域排名:{{ userInfo.totalRegionRank }}</text>
            <text>全国排名:{{ userInfo.totalRank }}</text>
          </view>
        </view>
        <view class="data-info" style="margin-bottom: 108rpx;">
          <view class="data-info-title">练习数据概览</view>
          <view class="data-info-line1">
            <view style="font-weight: 600;">练习进度:</view>
            <view class="progress-bar">
              <view class="progress-bar-inner" :style="{ width: ((userInfo.practice && userInfo.practice.progress) || 0) + '%' }"><text class="progress-bar-text">{{ userInfo.practice ? userInfo.practice.progress : 0 }}%</text></view>
            </view>
            <view class="progress-bar-tip">{{ userInfo.practice ? userInfo.practice.completedQuestions : 0 }}/{{ userInfo.practice ? userInfo.practice.totalQuestions : 0 }}</view>
          </view>
          <view class="data-info-line2">
            <view class="left">累计得分:{{ userInfo.practice ? userInfo.practice.totalScore : 0 }}</view>
            <view class="right">准确率:{{ userInfo.practice ? userInfo.practice.correctRate : 0 }}%</view>
          </view>
          <view class="data-info-line3">
            <view class="left">练习区域排名:{{ userInfo.practice ? userInfo.practice.regionRank : '-' }}</view>
            <view class="right">练习全国排名:{{ userInfo.practice ? userInfo.practice.nationalRank : '-' }}</view>
          </view>
          <view class="data-info-line4">
            <text>超过全国{{ userInfo.practice ? userInfo.practice.exceedPercent : 0 }}%的伙伴~</text>
          </view>
        </view>
        <view class="data-info">
          <view class="data-info-title">考试数据概览</view>
          <view class="data-info-line1">
            <view>考试进度:</view>
            <view class="progress-bar">
              <view class="progress-bar-inner" :style="{ width: ((userInfo.exam && userInfo.exam.progress) || 0) + '%' }"><text class="progress-bar-text">{{ userInfo.exam ? userInfo.exam.progress : 0 }}%</text></view>
            </view>
            <view class="progress-bar-tip">{{ userInfo.exam ? userInfo.exam.completedQuestions : 0 }}/{{ userInfo.exam ? userInfo.exam.totalQuestions : 0 }}</view>
          </view>
          <view class="data-info-line2">
            <view class="left">累计得分:{{ userInfo.exam ? userInfo.exam.totalScore : 0 }}</view>
            <view class="right">准确率:{{ userInfo.exam ? userInfo.exam.correctRate : 0 }}%</view>
          </view>
          <view class="data-info-line3">
            <view class="left">考试区域排名:{{ userInfo.exam ? userInfo.exam.regionRank : '-' }}</view>
            <view class="right">考试全国排名:{{ userInfo.exam ? userInfo.exam.nationalRank : '-' }}</view>
          </view>
          <view class="data-info-line4">
            <text>超过全国{{ userInfo.exam ? userInfo.exam.exceedPercent : 0 }}%的伙伴~</text>
          </view>
        </view>
      </view>
      <view class="rank-area" v-if="isCurrentUser">
        <view class="rank-title">
          <image
            src="/static/personal-top-left.png"
            mode="scaleToFill"
          />
          <text>美力全开热力榜</text>
          <image
            src="/static/personal-top-right.png"
            mode="scaleToFill"
          />
        </view>
        <view class="rank-tabs">
          <view class="rank-tab" v-for="(item, index) in tabs" :key="index" :class="{ active: activeTabIndex === index }" @tap="handleTabClick(index)">{{ item }}</view>
        </view>
        <view class="rank" v-if="activeTabIndex === 0">
          <view class="top-three-box">
            <!-- 左：第2名/group -->
            <view class="top-item rank-two">
              <UserAvatar v-if="(top3 && top3[1] && top3[1].isTieGroup)" :avatar-image="'/static/default-avatar.jpg'" :title="(top3 && top3[1] && top3[1].title) || ''" size="medium" @tap="openTieModal((top3 && top3[1]) ? top3[1].totalScore : null, 'personal', 2)" />
              <UserAvatar v-else :avatar-image="(top3 && top3[1]) ? top3[1].avatarImage || '/static/default-avatar.jpg' : '/static/default-avatar.jpg'" :title="(top3 && top3[1] && top3[1].title) || ''" size="medium" @tap="top3 && top3[1] && !top3[1].isTieGroup && navigateToPersonal(top3[1].workNumber)" />
              <text class="name">
                <template v-if="(top3 && top3[1] && top3[1].isTieGroup)">多名并列</template>
                <template v-else>{{ (top3 && top3[1]) ? top3[1].name : '--' }}</template>
              </text>
              <text class="score">总分: {{ (top3 && top3[1]) ? top3[1].totalScore : '--' }}</text>
            </view>
            <!-- 中：第1名/group -->
            <view class="top-item rank-one">
              <UserAvatar v-if="(top3 && top3[0] && top3[0].isTieGroup)" :avatar-image="'/static/default-avatar.jpg'" :title="(top3 && top3[0] && top3[0].title) || ''" size="medium" @tap="openTieModal((top3 && top3[0]) ? top3[0].totalScore : null, 'personal', 1)" />
              <UserAvatar v-else :avatar-image="(top3 && top3[0]) ? top3[0].avatarImage || '/static/default-avatar.jpg' : '/static/default-avatar.jpg'" :title="(top3 && top3[0] && top3[0].title) || ''" size="medium" @tap="top3 && top3[0] && !top3[0].isTieGroup && navigateToPersonal(top3[0].workNumber)" />
              <text class="name">
                <template v-if="(top3 && top3[0] && top3[0].isTieGroup)">多名并列</template>
                <template v-else>{{ (top3 && top3[0]) ? top3[0].name : '--' }}</template>
              </text>
              <text class="score">总分: {{ (top3 && top3[0]) ? top3[0].totalScore : '--' }}</text>
            </view>
            <!-- 右：第3名/group -->
            <view class="top-item rank-three">
              <UserAvatar v-if="(top3 && top3[2] && top3[2].isTieGroup)" :avatar-image="'/static/default-avatar.jpg'" :title="(top3 && top3[2] && top3[2].title) || ''" size="medium" @tap="openTieModal((top3 && top3[2]) ? top3[2].totalScore : null, 'personal', 3)" />
              <UserAvatar v-else :avatar-image="(top3 && top3[2]) ? top3[2].avatarImage || '/static/default-avatar.jpg' : '/static/default-avatar.jpg'" :title="(top3 && top3[2] && top3[2].title) || ''" size="medium" @tap="top3 && top3[2] && !top3[2].isTieGroup && navigateToPersonal(top3[2].workNumber)" />
              <text class="name">
                <template v-if="(top3 && top3[2] && top3[2].isTieGroup)">多名并列</template>
                <template v-else>{{ (top3 && top3[2]) ? top3[2].name : '--' }}</template>
              </text>
              <text class="score">总分: {{ (top3 && top3[2]) ? top3[2].totalScore : '--' }}</text>
            </view>
          </view>
        </view>
        <view class="team-rank" v-if="activeTabIndex === 1">
          <view v-for="(team, idx) in teamTop3" :key="idx" class="team-rank-item">
            <view class="team-rank-item-left">
              <template v-if="team.isTieGroup">
                <image class="avatar" :src="team.avatar || '/static/team-top' + (idx+1) + '.png'"></image>
                <view class="avatar-btn" style="margin-right: 20rpx;" @tap="openTieModal(team.totalScore, 'team', idx+1)">查看</view>
                <text class="name">多名并列</text>
              </template>
              <template v-else>
                <image class="avatar" :src="team.avatar || '/static/team-top' + (idx+1) + '.png'"></image>
                <text class="name">{{ team.storeName }}</text>
              </template>
            </view>
            <text class="score">{{ team.totalScore }}</text>
          </view>
        </view>
        <!-- 并列弹窗 -->
        <view v-if="showTieModal" class="modal-overlay">
          <view class="modal-box">
            <view class="modal-header">
              <text>{{ tieTitle }}</text>
              <view class="close-btn" @tap="closeTieModal">关闭</view>
            </view>
            <scroll-view class="modal-list" scroll-y="true">
              <view class="team-list-item" v-for="(item, idx) in tieModalList" :key="idx" @tap="item.workNumber ? navigateToPersonal(item.workNumber) : null">
                <text class="partner-name">{{ item.name }}</text>
                <text class="score-value">{{ item.totalScore }}</text>
              </view>
            </scroll-view>
          </view>
        </view>
        <view class="rank-btn" @tap="navigateToPartnerRank">查看完整榜单</view>
      </view>

    </PublicContentLayout>
  </view>
</template>

<script>
import CustomNavbar from '@/components/CustomNavbar.vue'
import PublicContentLayout from '@/components/PublicContentLayout.vue'
import UserAvatar from '@/components/UserAvatar.vue'
import { searchDeclaration, publishDeclaration, searchPersonalInfo } from '@/api/base.js'
import createLiteTable from '@/utils/tableLite'
import AllUserInfo from '../../static/AllUserInfo'

export default {
  components: { CustomNavbar, PublicContentLayout, UserAvatar },
  data() {
    return {
      displayedDeclaration: '',
      tabs: ['热力伙伴TOP50', '热力团队TOP15'],
      activeTabIndex: 0,
      isCurrentUser: false,
      isShowLookBox: false,
      userInfo: {
        flowerName: '',
        track: '',
        secondaryRegion: '',
        title: '',
        position: '', // 新增岗位字段，用于在赛道后显示
        totalScore: 0,
        totalRank: '-',
        totalRegionRank: '-',
        practice: {
          nationalRank: '-',
          regionRank: '-',
          totalScore: 0,
          correctQuestions: 0,
          totalQuestions: 0,
          completedQuestions: 0,
          correctRate: 0,
          progress: 0,
          exceedPercent: 0,
        },
        exam: {
          nationalRank: '-',
          regionRank: '-',
          totalScore: 0,
          correctQuestions: 0,
          totalQuestions: 0,
          completedQuestions: 0,
          correctRate: 0,
          progress: 0,
          exceedPercent: 0,
        },
        declaration: '', // 新增声明字段
      },
      // 个人总分前3名（按分组展示，可能为并列组）
      top3: [],
      // full table for modal filtering
      tableInfo: [],
      showTieModal: false,
      tieTitle: '',
      tieModalList: [],
      // 门店总分前三名
      teamTop3: [],
      editingDeclaration: false,
      lastDeclarationTap: 0,
      isLoaded: false, // Add isLoaded to data
      longLoading: false,
      longLoadingTimer: null,
    }
  },
  onLoad(options) {
    // start long-loading timer in case data load takes long
    try { this.startLongLoadingTimer && this.startLongLoadingTimer(); } catch (e) {}
    dd.getStorage({
      key: 'CurrentUserWorkNumber',
      success: (res) => {
        const stored = res && res.data ? String(res.data) : '';
        const passed = options && options.workNumber ? String(options.workNumber) : '';
        const targetWorkNumber = passed || stored || null;
        this.isCurrentUser = (stored && passed) ? (passed === stored) : (targetWorkNumber === stored);
        console.log(options.workNumber, "options.workNumber");
        console.log(res, "CurrentUserWorkNumber");
        this.getTableInfo(targetWorkNumber);
      },
      fail: () => {
        // fallback: use passed option if available
        const targetWorkNumber = (options && options.workNumber) ? String(options.workNumber) : null;
        this.isCurrentUser = false;
        this.getTableInfo(targetWorkNumber);
      },
    });
    // read whether current environment/user is a participant and only then show activity rule button
    try {
      dd.getStorage({
        key: 'isShowLookBox',
        success: (r) => { this.isShowLookBox = !!r.data; },
        fail: () => { this.isShowLookBox = false; }
      });
    } catch (e) {}
  },
  onReady() {
    // 修复Android渲染问题
    setTimeout(() => { dd.setNavigationBar({ title: '' }); }, 100);
    const menu = dd.getMenuButtonBoundingClientRect && dd.getMenuButtonBoundingClientRect();
    if (menu) {
      console.log(menu, "胶囊按钮位置");
    } else {
      console.log("dd.getMenuButtonBoundingClientRect 不可用，跳过胶囊按钮位置检测");
    }
  },
  methods: {
    /**
     * Persist split TableInfo into storage when large.
     * If list.length > chunkSize (800) it will write TableInfo1..N and set TableInfo_count.
     * Returns a Promise that resolves once writes complete.
     */
    saveTableInfo(list) {
      return new Promise((resolve) => {
        try {
          const chunkSize = 300;
          const chunks = [];
          const arr = Array.isArray(list) ? list : (Array.isArray(list.data) ? list.data : []);
          for (let i = 0; i < arr.length; i += chunkSize) {
            chunks.push(arr.slice(i, i + chunkSize));
          }
          if (chunks.length === 0) chunks.push([]);
          const newCount = chunks.length;

          try {
            dd.getStorage({
              key: 'TableInfo_count',
              success: (res) => {
                const oldCount = Number(res && res.data) || 0;
                if (oldCount > newCount) {
                  for (let k = newCount + 1; k <= oldCount; k++) {
                    try { dd.removeStorage({ key: 'TableInfo' + k }); } catch (e) {}
                  }
                }
              },
              fail: () => {},
            });
          } catch (e) {}

          const setPromises = chunks.map((chunk, idx) => new Promise((resSet) => {
            try {
              dd.setStorage({
                key: 'TableInfo' + (idx + 1),
                data: createLiteTable(chunk),
                success: () => resSet(),
                fail: () => resSet(),
              });
            } catch (e) { resSet(); }
          }));

          Promise.all(setPromises).then(() => {
            try {
              dd.setStorage({ key: 'TableInfo_count', data: newCount, success: () => resolve(), fail: () => resolve() });
            } catch (e) { resolve(); }
          }).catch(() => {
            try { dd.setStorage({ key: 'TableInfo_count', data: newCount, success: () => resolve(), fail: () => resolve() }); } catch (e) { resolve(); }
          });
        } catch (e) { resolve(); }
      });
    },

    /**
     * Load TableInfo by reading TableInfo_count and then TableInfo1..N and concatenating.
     * Falls back to legacy single 'TableInfo' key if TableInfo_count not present.
     * Resolves to an array (possibly empty).
     */
    loadTableInfo() {
      return new Promise((resolve) => {
        try {
          dd.getStorage({
            key: 'TableInfo_count',
            success: (res) => {
              const count = Number(res && res.data) || 0;
              if (count <= 0) { resolve([]); return; }
              const gets = [];
              for (let i = 1; i <= count; i++) {
                gets.push(new Promise((resGet) => {
                  try {
                    dd.getStorage({ key: 'TableInfo' + i, success: (r) => resGet(Array.isArray(r.data) ? r.data : (r.data || [])), fail: () => resGet([]) });
                  } catch (e) { resGet([]); }
                }));
              }
              Promise.all(gets).then((parts) => {
                try { resolve([].concat(...parts)); } catch (e) { resolve([]); }
              }).catch(() => resolve([]));
            },
            fail: () => {
              try {
                dd.getStorage({
                  key: 'TableInfo',
                  success: (r) => { resolve(Array.isArray(r.data) ? r.data.slice() : (r.data || [])); },
                  fail: () => { resolve([]); }
                });
              } catch (e) { resolve([]); }
            },
          });
        } catch (e) { resolve([]); }
      });
    },

    // Safe loader that falls back to legacy single-key storage when loadTableInfo isn't available
    safeLoadTableInfo() {
      if (typeof this.loadTableInfo === 'function') return this.loadTableInfo();
      return new Promise((resolve) => {
        try {
          dd.getStorage({ key: 'TableInfo', success: (r) => { resolve(Array.isArray(r.data) ? r.data.slice() : (r.data || [])); }, fail: () => { resolve([]); } });
        } catch (e) { resolve([]); }
      });
    },

    // Start a timer to show an extended-loading note after 3s
    startLongLoadingTimer() {
      try {
        this.longLoading = false;
        if (this.longLoadingTimer) clearTimeout(this.longLoadingTimer);
        this.longLoadingTimer = setTimeout(() => { this.longLoading = true; this.longLoadingTimer = null; }, 3000);
      } catch (e) { this.longLoading = false; this.longLoadingTimer = null; }
    },

    // Clear any running long-loading timer and reset flag
    clearLongLoadingTimer() {
      try {
        if (this.longLoadingTimer) { clearTimeout(this.longLoadingTimer); this.longLoadingTimer = null; }
      } catch (e) {}
      this.longLoading = false;
    },

    /**
     * 获取表格信息并根据员工号获取对应信息
     */
    getTableInfo(workNumber) {
      // New implementation: fetch personal info from backend and use the returned `msg` payload directly
      const self = this;
      this.startLongLoadingTimer && this.startLongLoadingTimer();
      if (!workNumber) {
        self.isLoaded = true;
        this.clearLongLoadingTimer && this.clearLongLoadingTimer();
        return;
      }
      try {
        const wn = Number(workNumber);
        searchPersonalInfo(wn).then((resp) => {
          let body = resp;
          if (typeof body === 'string') {
            try { body = JSON.parse(body); } catch (e) { /* ignore */ }
          }
          const msg = (body && (body.msg || body.data)) ? (body.msg || body.data) : (body || {});

          const toNumber = (v) => { const n = Number(v); return Number.isFinite(n) ? n : 0; };
          const normalizeRate = (r) => {
            if (r === null || r === undefined) return 0;
            if (typeof r === 'string') {
              const n = parseFloat(String(r).replace('%', '').trim());
              return Number.isFinite(n) ? n : 0;
            }
            const n = Number(r);
            return Number.isFinite(n) ? n : 0;
          };
          const normalizeProgress = (p) => {
            if (p === null || p === undefined) return 0;
            if (typeof p === 'string') {
              if (p.indexOf('%') >= 0) return parseFloat(p.replace('%', '').trim());
              const n = Number(p);
              return Number.isFinite(n) ? n : 0;
            }
            const n = Number(p);
            if (!Number.isFinite(n)) return 0;
            if (n > 0 && n <= 1) return Number((n * 100).toFixed(2));
            return n;
          };

          // Map backend message fields directly onto userInfo
          const user = {
            flowerName: msg.flowerName || '',
            workNumber: msg.workNumber || wn,
            track: msg.track || '',
            position: msg.position || '',
            title: msg.title || '',
            primaryRegion: msg.primaryRegion || '',
            secondaryRegion: msg.secondaryRegion || '',
            declaration: msg.declaration || '',
            totalScore: toNumber(msg.score || msg.totalScore),
            totalRank: (msg.countryRank !== undefined && msg.countryRank !== null) ? msg.countryRank : '-',
            totalRegionRank: (msg.regionRank !== undefined && msg.regionRank !== null) ? msg.regionRank : '-',
            totalCorrectRate: normalizeRate(msg.totalCorrectRate) || 0,
            totalCompletedQuestions: msg.totalCompletedQuestions || 0,
            totalCorrectQuestions: msg.totalCorrectQuestions || 0,
            totalTotalQuestions: msg.totalTotalQuestions || 0,
            declaration: msg.declaration || ''
          };

          user.practice = {
            nationalRank: (msg.practiceCountryRank !== undefined && msg.practiceCountryRank !== null) ? msg.practiceCountryRank : '-',
            regionRank: (msg.practiceRegionRank !== undefined && msg.practiceRegionRank !== null) ? msg.practiceRegionRank : '-',
            totalScore: toNumber(msg.practiceScore),
            correctQuestions: msg.practiceCorrectQuestions || 0,
            totalQuestions: msg.practiceTotalQuestions || 0,
            completedQuestions: msg.practiceCompletedQuestions || 0,
            correctRate: normalizeRate(msg.practiceCorrectRate),
            progress: normalizeProgress(msg.practiceProgress),
            exceedPercent: normalizeRate(msg.practiceExceedRate)
          };

          user.exam = {
            nationalRank: (msg.examCountryRank !== undefined && msg.examCountryRank !== null) ? msg.examCountryRank : '-',
            regionRank: (msg.examRegionRank !== undefined && msg.examRegionRank !== null) ? msg.examRegionRank : '-',
            totalScore: toNumber(msg.examScore),
            correctQuestions: msg.examCorrectQuestions || 0,
            totalQuestions: msg.examTotalQuestions || 0,
            completedQuestions: msg.examCompletedQuestions || 0,
            correctRate: normalizeRate(msg.examCorrectRate),
            progress: normalizeProgress(msg.examProgress),
            exceedPercent: normalizeRate(msg.examExceedRate)
          };

          // Attach avatar/name/title from AllUserInfo when available
          try {
            if (typeof AllUserInfo !== 'undefined' && Array.isArray(AllUserInfo)) {
              const mainInfo = AllUserInfo.find(u => String(u.staffId) === String(user.workNumber) || String(u.workNumber) === String(user.workNumber));
              if (mainInfo) {
                user.avatarImage = mainInfo.avatarImage || mainInfo.avatar || '/static/default-avatar.jpg';
                user.flowerName = user.flowerName || mainInfo.name || mainInfo.flowerName || user.flowerName;
                user.title = user.title || mainInfo.title || user.title || '';
              } else {
                user.avatarImage = user.avatarImage || '/static/default-avatar.jpg';
              }
            } else {
              user.avatarImage = user.avatarImage || '/static/default-avatar.jpg';
            }
          } catch (e) { user.avatarImage = user.avatarImage || '/static/default-avatar.jpg'; }

          // Use server-provided top partner/team lists and scores directly
          const findUserInfo = (id) => {
            if (!id) return null;
            const s = String(id);
            try {
              const found = AllUserInfo.find(u => String(u.staffId) === s || String(u.workNumber) === s || String(u.staff_id) === s || String(u.id) === s);
              return found || null;
            } catch (e) { return null; }
          };

          const buildPartner = (listKey, scoreKey) => {
            // Support both legacy plain id arrays and new "id-title" entries (e.g. "626038-匠人").
            const rawList = Array.isArray(msg[listKey]) ? msg[listKey] : [];
            // Normalize into objects { id: string, title: string | null }
            const parsedList = rawList.map((it) => {
              if (it === null || it === undefined) return null;
              const s = String(it);
              const dashIndex = s.indexOf('-');
              if (dashIndex > 0) {
                const idPart = s.substring(0, dashIndex);
                const titlePart = s.substring(dashIndex + 1);
                return { id: idPart, title: titlePart || null };
              }
              return { id: s, title: null };
            }).filter(Boolean);

            const score = msg[scoreKey] || 0;

            // Single partner (not a tie)
            if (parsedList.length === 1) {
              const uid = parsedList[0].id;
              const parsedTitle = parsedList[0].title;
              const info = findUserInfo(uid);
              return {
                isTieGroup: false,
                totalScore: toNumber(score),
                membersCount: 1,
                workNumber: uid,
                name: (info && (info.name || info.flowerName)) ? (info.name || info.flowerName) : (msg.partnerName || '--'),
                avatarImage: (info && (info.avatarImage || info.avatar)) ? (info.avatarImage || info.avatar) : (msg.partnerAvatar || '/static/default-avatar.jpg'),
                members: [uid],
                // prefer server-provided / AllUserInfo title, fall back to parsed title
                title: (info && info.title) ? info.title : (parsedTitle || (msg.partnerTitle || ''))
              };
            }

            // Multi-member tie group
            return {
              isTieGroup: parsedList.length > 1,
              totalScore: toNumber(score),
              membersCount: parsedList.length,
              workNumber: null,
              name: '多名并列',
              avatarImage: '/static/default-avatar.jpg',
              members: parsedList.map(p => p.id)
            };
          };

          this.top3 = [
            buildPartner('firstPartnerList', 'firstPartnerScore'),
            buildPartner('secondPartnerList', 'secondPartnerScore'),
            buildPartner('thirdPartnerList', 'thirdPartnerScore')
          ];

          const buildTeam = (nameListKey, scoreKey) => {
            const names = Array.isArray(msg[nameListKey]) ? msg[nameListKey] : [];
            const score = msg[scoreKey] || 0;
            return {
              isTieGroup: names.length > 1,
              storeName: (names.length === 1 ? names[0] : '多名并列'),
              totalScore: score,
              members: names
            };
          };

          this.teamTop3 = [
            buildTeam('firstTeamNameList', 'firstTeamScore'),
            buildTeam('secondTeamNameList', 'secondTeamScore'),
            buildTeam('thirdTeamNameList', 'thirdTeamScore')
          ];

          // clear local table usage -- server provides authoritative data now
          this.tableInfo = [];
          this.userInfo = user;
          this.displayedDeclaration = user.declaration || '';
          this.editingDeclaration = false;
          self.isLoaded = true;
          this.clearLongLoadingTimer && this.clearLongLoadingTimer();
        }).catch((err) => {
          console.error('searchPersonalInfo error', err);
          self.isLoaded = true;
          this.clearLongLoadingTimer && this.clearLongLoadingTimer();
        });
      } catch (e) {
        self.isLoaded = true;
        this.clearLongLoadingTimer && this.clearLongLoadingTimer();
      }
    },
    handleTabClick(index) {
      this.activeTabIndex = index;
    },
    openTieModal(score, type = 'personal',rank = null) {
      if (score === null || score === undefined) return;
      if (type === 'team') {
        this.tieTitle = `第${rank}名并列名单`;
        // Prefer server-provided teamTop3 if available (it contains members lists from backend)
        let teamGroup = null;
        if (rank) {
          teamGroup = this.teamTop3 && this.teamTop3[rank - 1] ? this.teamTop3[rank - 1] : null;
        }
        if (!teamGroup) {
          teamGroup = (this.teamTop3 || []).find(t => Number(t.totalScore) === Number(score));
        }
        if (teamGroup && Array.isArray(teamGroup.members) && teamGroup.members.length > 0) {
          this.tieModalList = teamGroup.members.map((name, idx) => ({
            rank: idx + 1,
            name: name || '--',
            totalScore: teamGroup.totalScore || score,
            workNumber: null,
            avatarImage: '/static/default-avatar.jpg',
            title: ''
          }));
          this.showTieModal = true;
          return;
        }

        // Fallback: compute stores from local tableInfo (legacy path)
        const table = Array.isArray(this.tableInfo) ? this.tableInfo.slice() : [];
        const toNumberLocal = (v) => { const n = Number(v); return Number.isFinite(n) ? n : 0; };
        const storeMapLocal = {};
        table.forEach(it => {
          const key = (it.storeName || it.store || it.team || it.shopName || '未知门店').toString();
          const sc = toNumberLocal(it.practiceTotalScore) + toNumberLocal(it.examTotalScore);
          if (!storeMapLocal[key]) {
            storeMapLocal[key] = { storeName: key, totalScore: 0, avatar: '' };
          }
          storeMapLocal[key].totalScore += sc;
          if (!storeMapLocal[key].avatar && it.storeAvatar) storeMapLocal[key].avatar = it.storeAvatar;
        });
        const stores = Object.keys(storeMapLocal).map(k => storeMapLocal[k]).filter(s => s.totalScore === score);
        this.tieModalList = stores.map((s, idx) => ({ rank: idx + 1, name: s.storeName, totalScore: s.totalScore, workNumber: null, avatarImage: s.avatar || '/static/default-avatar.jpg', title: '' }));
        this.showTieModal = true;
        return;
      }

      // personal/top3 handling - use members list from top3 and lookup AllUserInfo
      this.tieTitle = rank ? `第${rank}名并列名单` : `总分: ${score} 的并列名单`;
      // try to find the group in top3 by exact score or by rank
      let group = null;
      if (rank) {
        // rank provided (1/2/3) -> index rank-1
        group = this.top3 && this.top3[rank - 1] ? this.top3[rank - 1] : null;
      }
      if (!group) {
        group = (this.top3 || []).find(g => Number(g.totalScore) === Number(score));
      }
      const members = (group && Array.isArray(group.members) && group.members.length > 0) ? group.members : [];
      const tieList = members.map((m) => {
        const info = (typeof AllUserInfo !== 'undefined' && Array.isArray(AllUserInfo)) ? (AllUserInfo.find(u => String(u.staffId) === String(m) || String(u.workNumber) === String(m))) : null;
        return {
          name: info ? (info.name || info.flowerName || '--') : '--',
          totalScore: score,
          workNumber: m,
          avatarImage: info ? (info.avatarImage || info.avatar || '/static/default-avatar.jpg') : '/static/default-avatar.jpg',
          title: info ? (info.title || '') : ''
        };
      });
      this.tieModalList = tieList.map((p, idx) => ({ rank: idx + 1, name: p.name, totalScore: p.totalScore, workNumber: p.workNumber, avatarImage: p.avatarImage, title: p.title }));
      this.showTieModal = true;
    },
    closeTieModal() {
      this.showTieModal = false;
      this.tieModalList = [];
      this.tieTitle = '';
    },
    navigateToPersonal(workNumber) {
      uni.navigateTo({
        url: `/pages/personal/index?workNumber=${workNumber}`
      });
    },
    navigateToPartnerRank() {
      if (this.activeTabIndex === 0) {
        uni.navigateTo({
          url: '/pages/partnerRank/index'
        });
      } else {
        uni.navigateTo({
          url: '/pages/teamRank/index'
        });
      }
    },
    navigateToActivityRule() {
      uni.navigateTo({
        url: '/pages/rules/index?track=' + encodeURIComponent(this.userInfo.track || '') + '&role=' + encodeURIComponent(this.userInfo.position || '')
      });
    },
    handleDeclarationTap() {
      const now = Date.now();
      if (now - this.lastDeclarationTap <= 300) {
        // 双击（或连点）触发编辑
        this.lastDeclarationTap = 0;
        this.enableEditDeclaration();
      } else {
        this.lastDeclarationTap = now;
      }
    },
    // ensure displayedDeclaration is copied into editable model when editing starts
    enableEditDeclaration() {
      // copy read-only displayed declaration into editable userInfo.declaration
      try { this.userInfo.declaration = this.displayedDeclaration || this.userInfo.declaration || ''; } catch (e) {}
      this.editingDeclaration = true;
      this.$nextTick(() => {
        setTimeout(() => {
          try { this.$refs.declarationInput && this.$refs.declarationInput.focus && this.$refs.declarationInput.focus(); } catch (e) {}
        }, 50);
      });
    },
    submitDeclaration() {
      this.editingDeclaration = false;
      // load possibly-split TableInfo and update
      this.safeLoadTableInfo().then((resTable) => {
        const table = Array.isArray(resTable) ? resTable.slice() : [];
        const idx = table.findIndex(item => String(item.workNumber) === String(this.userInfo.workNumber));
        if (idx >= 0) {
          table[idx] = Object.assign({}, table[idx], { declaration: this.userInfo.declaration });
        } else {
          table.push({ workNumber: this.userInfo.workNumber, declaration: this.userInfo.declaration });
        }
        this.saveTableInfo(table).then(() => {
          console.log('Declaration updated successfully');
          this.displayedDeclaration = this.userInfo.declaration;
          try { publishDeclaration([{ workNumber: this.userInfo.workNumber, declaration: this.userInfo.declaration }]).then(() => { console.log('Declaration published to server'); }).catch((err) => { console.error('Failed to publish declaration:', err); }); } catch (e) {}
        }).catch((err) => {
          console.error('Failed to save declaration:', err);
          try { publishDeclaration([{ workNumber: this.userInfo.workNumber, declaration: this.userInfo.declaration }]).then(() => { console.log('Declaration published to server'); }).catch((err) => { console.error('Failed to publish declaration:', err); }); } catch (e) {}
        });
      }).catch(() => {
        // fallback: write a minimal table using save helper
        this.saveTableInfo([{ workNumber: this.userInfo.workNumber, declaration: this.userInfo.declaration }]).catch(() => {});
        this.displayedDeclaration = this.userInfo.declaration;
        try { publishDeclaration([{ workNumber: this.userInfo.workNumber, declaration: this.userInfo.declaration }]).then(() => { console.log('Declaration published to server'); }).catch((err) => { console.error('Failed to publish declaration:', err); }); } catch (e) {}
      });
    },
  }
}
</script>

<style scoped>
.container {
  width: 100%;
  min-height: 100vh;
  box-sizing: border-box;
  background: url('/static/personal-bg.png') no-repeat;
  background-size: 100% 100%;
  background-color: #000;
  padding-bottom: 170rpx;
  margin: 0 auto;
}
.banner-area {
  position: fixed;
  top: 280rpx;
  right: 0rpx;
}
.btn {
  position: relative;
  width: 136rpx;
  height: 64rpx;
  background: rgba(255,255,255,0.1);
  border-radius: 32rpx 0rpx 0rpx 32rpx;
}
.btn-text {
  position: absolute;
  top: 0;
  left: 0;
  width: 136rpx;
  height: 64rpx;
  line-height: 64rpx;
  font-size: 24rpx;
  font-weight: 400;
  color: #AA9167;
  padding-left: 16rpx;
  box-sizing: border-box;
}
.info {
  width: calc(100% - 48rpx);
  margin: 0 auto;
  display: flex;
  padding-top: 176rpx;
  height: 212rpx;
}
.info-left {
  position: relative;
  width: 172rpx;
  height: 172rpx;
  box-sizing: border-box;
}
.info-right {
  padding: 32rpx 0 0 8rpx;
}
.info-right-name {
  height: 36rpx;
  line-height: 36rpx;
  font-size: 28rpx;
  font-weight: 600;
  color: #FBEDD4;
  margin-bottom: 8rpx;
}
.info-right-lb {
  display: inline-block;
  /* width: 168rpx; */
  height: 48rpx;
  line-height: 48rpx;
  background: url('/static/personal-lb-bg.png') no-repeat;
  background-size: 100% 100%;
  background-position: right;
  font-size: 24rpx;
  font-weight: 600;
  color: #3D2A2A;
  padding-left: 16rpx;
  box-sizing: border-box;
  margin-left: 16rpx;
  padding-right: 20rpx;
  border-radius: 30rpx 0 0 30rpx;
}
.info-right-city {
  height: 40rpx;
  line-height: 40rpx;
  font-size: 24rpx;
  color: #FBEDD4;
  margin-bottom: 4rpx;
}
.info-right-declaration {
  width: 400rpx;
  font-size: 24rpx;
  color: #AA9167;
}
.info-right-declaration-input {
  width: 100%;
  height: 40rpx;
  line-height: 40rpx;
  font-size: 24rpx;
  color: #FBEDD4;
  background-color: #3E382F;
  border: 1rpx solid #544840;
  border-radius: 8rpx;
  padding: 0 10rpx;
  box-sizing: border-box;
}
.info-avatar {
  width: 132rpx;
  height: 132rpx;
  border-radius: 15rpx;
}
.info-avatar-gj {
  position: absolute;
  top: 0;
  left: 0;
  width: 172rpx;
  height: 172rpx;
  z-index: 9;
}
.data {
  width: calc(100% - 48rpx);
  background: url('/static/personal-data-bg.png') no-repeat center center;
  background-size: 100% 100%;
  margin: 0 auto 20rpx;
  box-sizing: border-box;
  padding-bottom: 40rpx;
}
.data-top {
  padding: 22rpx 24rpx 0;
  margin-bottom: 48rpx;
}
.data-top-title {
  height: 40rpx;
  line-height: 40rpx;
  font-size: 32rpx;
  font-weight: 600;
  color: #FBEDD4;
  margin-bottom: 20rpx;
  text-align: center;
}
.data-top-rank {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64rpx;
  background: #544840;
  border-radius: 8rpx 8rpx 8rpx 8rpx;
  padding: 0 24rpx;
  font-size: 24rpx;
  color: #FBEDD4;
}
.data-info {
  padding-left: 24rpx;
}
.data-info .left {
  flex: 1;
}
.data-info .right {
  flex: 1;
}
.data-info-title {
  height: 40rpx;
  line-height: 40rpx;
  font-size: 28rpx;
  font-weight: 600;
  color: #FBEDD4;
  margin-bottom: 26rpx;
}
.data-info-line1 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 40rpx;
  font-size: 28rpx;
  color: #D8D8D8;
  margin-bottom: 20rpx;
  padding: 0 18rpx 0 0;
}
.data-info-line2 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 40rpx;
  font-size: 28rpx;
  color: #D8D8D8;
  margin-bottom: 20rpx;
}
.data-info-line3 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 40rpx;
  font-size: 28rpx;
  color: #D8D8D8;
  margin-bottom: 26rpx;
}
.data-info-line4 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 28rpx;
  color: #D8D8D8;
  height: 40rpx;
}
.progress-bar {
  width: 348rpx;
  height: 24rpx;
  background: #3E382F;
  border-radius: 20rpx;
}
.progress-bar-text {
  transform: scale(0.8);
  font-size: 24rpx;
  color: #3E382F;
}
.progress-bar-tip {
  font-size: 24rpx;
  color: #D8D8D8;
}
.progress-bar-inner {
  width: 65%;
  height: 24rpx;
  background: linear-gradient( 270deg, #E3CBB4 0%, #A5836C 100%);
  border-radius: 20rpx;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

.rank-area {
  width: calc(100% - 48rpx);
  margin: 0 auto;
  background: linear-gradient( 360deg, #241F17 0%, #352E23 97%);
  border-radius: 24rpx;
  padding: 20rpx 24rpx 24rpx;
  box-sizing: border-box;
}
.rank-title {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 44rpx;
  font-size: 32rpx;
  margin: 0 0 24rpx;
}
.rank-title image {
  width: 44rpx;
  height: 44rpx;
}
.rank-title text {
  font-size: 32rpx;
  font-weight: 600;
  color: #FBEDD4;
  position: relative;
  top: 4rpx;
  margin: 0 4rpx;
}
.rank-tabs {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 520rpx;
  height: 56rpx;
  margin-bottom: 40rpx;
  background: #3E382F;
  border-radius: 36rpx;
  margin: 0 auto;
  overflow: hidden;
}

.rank-tab {
  width: 260rpx;
  height: 56rpx;
  font-size: 24rpx;
  color: #D8D8D8;
  text-align: center;
  line-height: 56rpx;
}
.rank-tab.active {
  background: url('/static/btn-bg.png') no-repeat center center;
  background-size: 100% 100%;
  font-size: 24rpx;
  font-weight: 600;
  color: #3D2A2A;
  border-radius: 36rpx;
}
.top-three-box {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  margin: 40rpx 0 24rpx;
}

.top-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  width: 206rpx; 
  height: 278rpx;
  border-radius: 16rpx;
  box-sizing: border-box;
  position: relative;
  color: #333;
  font-size: 28rpx;
  font-weight: bold;
  padding-top: 74rpx;
  box-sizing: border-box;
}

.top-item.rank-one {
  height: 318rpx;
  background: url('/static/rank-top1-bg.png') no-repeat center center;
  background-size: 100% 100%;
  margin-bottom: 0rpx;
  padding-top: 80rpx;
}

.top-item.rank-two {
  background: url('/static/rank-top2-bg.png') no-repeat center center;
  background-size: 100% 100%;
}

.top-item.rank-three {
  background: url('/static/rank-top3-bg.png') no-repeat center center;
  background-size: 100% 100%;
}

.top-item .avatar {
  width: 80rpx;
  height: 80rpx;
  border-radius: 16rpx;
  margin-bottom: 8rpx;
  overflow: hidden;
}

.top-item .name {
  font-size: 28rpx;
  font-weight: 600;
  color: #222;
  margin-bottom: 8rpx;
}

.top-item .score {
  font-size: 24rpx;
  font-weight: 400;
  color: #222;
}

.top-item .avatar-btn {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 80rpx;
  height: 80rpx;
  border-radius: 16rpx;
  margin-bottom: 8rpx;
  background-color: #999;
  overflow: hidden;
}

.rank-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto;
  width: 252rpx;
  height: 64rpx;
  border-radius: 40rpx 40rpx 40rpx 40rpx;
  border: 2rpx solid #D8D8D8;
  font-size: 28rpx;
  color: #D8D8D8;
}
.team-rank {
  padding-top: 40rpx;
}
.team-rank-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 72rpx;
  border-radius: 8rpx 8rpx 8rpx 8rpx;
  font-size: 24rpx;
  color: #FBEDD4;
  margin-bottom: 20rpx;
}
.team-rank-item-left {
  display: flex;
  align-items: center;
}
.team-rank-item .avatar {
  width: 72rpx;
  height: 72rpx;
  margin-right: 30rpx;
}
.team-rank-item .name {
  font-size: 24rpx;
  font-weight: 600;
  color: #FBEDD4;
}
.team-rank-item .score {
  font-size: 24rpx;
  font-weight: 600;
  color: #FBEDD4;
}

/* 并列弹窗样式 */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.modal-box {
  width: 600rpx;
  background: #3E382F;
  border-radius: 24rpx;
  padding: 40rpx;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  margin-bottom: 30rpx;
}

.modal-header text {
  font-size: 36rpx;
  font-weight: 600;
  color: #FBEDD4;
  text-align: center;
}

.close-btn {
  font-size: 28rpx;
  color: #D8D8D8;
  padding: 10rpx 20rpx;
  border-radius: 30rpx;
  border: 2rpx solid #D8D8D8;
}

.modal-list {
  width: 100%;
  max-height: 600rpx; /* 控制弹窗高度 */
  overflow-y: auto;
}

.team-list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 80rpx;
  padding: 0 20rpx;
  border-bottom: 1rpx solid #544840;
}

.team-list-item:last-child {
  border-bottom: none;
}

.rank-column {
  width: 60rpx;
  text-align: center;
  font-size: 28rpx;
  color: #D8D8D8;
}

.avatar-small {
  width: 60rpx;
  height: 60rpx;
  border-radius: 30rpx;
  margin-right: 20rpx;
  overflow: hidden;
}

.partner-name {
  flex: 1;
  font-size: 28rpx;
  color: #FBEDD4;
  margin-right: 20rpx;
}

.score-value {
  font-size: 28rpx;
  font-weight: 600;
  color: #FBEDD4;
}

/* 全局加载遮罩样式 */
.loading-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 999;
}

.loading-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40rpx;
  background: #3E382F;
  border-radius: 24rpx;
}

.spinner {
  display: flex;
  justify-content: space-around;
  width: 100rpx;
  height: 100rpx;
  margin-bottom: 20rpx;
}

.dot {
  width: 20rpx;
  height: 20rpx;
  background-color: #FBEDD4;
  border-radius: 50%;
  animation: bounce 1.4s infinite ease-in-out;
}

.dot:nth-child(1) {
  animation-delay: -0.32s;
}

.dot:nth-child(2) {
  animation-delay: -0.16s;
}

.dot:nth-child(3) {
  animation-delay: 0s;
}

@keyframes bounce {
  0%, 80%, 100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-20rpx);
  }
}

.loading-text {
  font-size: 28rpx;
  color: #FBEDD4;
}
</style> 